{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bd1491a2",
   "metadata": {},
   "source": [
    "# 基于SpringBoot+Python的多语言前后端智能多人聊天系统第4课书面作业\n",
    "学号：114498  \n",
    "\n",
    "**作业内容：**  \n",
    "参考课程，再自己的环境跑下课程对话机器人代码（代码和运行结果截图即可）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3edfdaed",
   "metadata": {},
   "source": [
    "## 作业情况"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf7ac2d4",
   "metadata": {},
   "source": [
    "基于课程提供的智能客服系统做了一些修改：  \n",
    "* Django版本与课程上不完全相同，因此运行部署上做一些调整。  \n",
    "* tensorflow版本也不相同，对应的seq2seq+attention训练及预测模型需要调整。\n",
    "* 调试系统，如中文的适应情况。\n",
    "\n",
    "过程中会截图并附上代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efdd24de",
   "metadata": {},
   "source": [
    "### 1 部署环境适配\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa3186c9",
   "metadata": {},
   "source": [
    "1. 安装Django, \"pip install Django\"，即可安装，我将Django安装在我的tensorflow虚拟环境中。  \n",
    "2. 的Django版本为3.2.9, tensorflow版本为2.5.0，与课程上的版本相去甚远。因此要做一些调整。  \n",
    "* 按提示用\"python manage.py migration\"命令做一些数据迁移  \n",
    "* 安装一些运行需要的包，如tqdm等  \n",
    "* 修改seq2seq+attention模型，使之支持tensorflow 2.5.0版本(下一章节详细描述)  \n",
    "3. 运行“python manage.py runserver”，即可运行。截图如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f17ce18",
   "metadata": {},
   "source": [
    "![runserver](https://gitee.com/dotzhen/cloud-notes/raw/master/%E6%89%B9%E6%B3%A8%202021-11-20%20170855.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cdfe7d3",
   "metadata": {},
   "source": [
    "### 2 模型代码适配修改\n",
    "修改seq2seq+attention模型，使之支持tensorflow 2.5.0版本。\n",
    "完整代码参见：https://gitee.com/dotzhen/SprintBoot_Class/tree/master/Class04/zhinengkefu\n",
    "\n",
    "截取部署关键代码如下：\n",
    "1. trainModel.py中描述seq2seq+attention部分模型代码如下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01cfe572",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Encoder(keras.Model):\n",
    "    def __init__(self, vocab_size, embedding_dim, hidden_units):\n",
    "        super(Encoder, self).__init__()\n",
    "        # Embedding Layer\n",
    "        self.embedding = Embedding(vocab_size, embedding_dim, mask_zero=True)\n",
    "        # Encode LSTM Layer\n",
    "        self.encoder_lstm = LSTM(hidden_units, return_sequences=True, return_state=True, name=\"encode_lstm\")\n",
    "\n",
    "    def call(self, inputs):\n",
    "        encoder_embed = self.embedding(inputs)\n",
    "        encoder_outputs, state_h, state_c = self.encoder_lstm(encoder_embed)\n",
    "        return encoder_outputs, state_h, state_c\n",
    "\n",
    "\n",
    "class Decoder(keras.Model):\n",
    "    def __init__(self, vocab_size, embedding_dim, hidden_units):\n",
    "        super(Decoder, self).__init__()\n",
    "        # Embedding Layer\n",
    "        self.embedding = Embedding(vocab_size, embedding_dim, mask_zero=True)\n",
    "        # Decode LSTM Layer\n",
    "        self.decoder_lstm = LSTM(hidden_units, return_sequences=True, return_state=True, name=\"decode_lstm\")\n",
    "        # Attention Layer\n",
    "        self.attention = Attention()\n",
    "\n",
    "    def call(self, enc_outputs, dec_inputs, states_inputs):\n",
    "        decoder_embed = self.embedding(dec_inputs)\n",
    "        dec_outputs, dec_state_h, dec_state_c = self.decoder_lstm(decoder_embed, initial_state=states_inputs)\n",
    "        attention_output = self.attention([dec_outputs, enc_outputs])\n",
    "\n",
    "        return attention_output, dec_state_h, dec_state_c\n",
    "\n",
    "\n",
    "def Seq2Seq(maxlen, embedding_dim, hidden_units, vocab_size):\n",
    "    \"\"\"\n",
    "    seq2seq model\n",
    "    \"\"\"\n",
    "    # Input Layer\n",
    "    encoder_inputs = Input(shape=(maxlen,), name=\"encode_input\")\n",
    "    decoder_inputs = Input(shape=(None,), name=\"decode_input\")\n",
    "    # Encoder Layer\n",
    "    encoder = Encoder(vocab_size, embedding_dim, hidden_units)\n",
    "    enc_outputs, enc_state_h, enc_state_c = encoder(encoder_inputs)\n",
    "    dec_states_inputs = [enc_state_h, enc_state_c]\n",
    "    # Decoder Layer\n",
    "    decoder = Decoder(vocab_size, embedding_dim, hidden_units)\n",
    "    attention_output, dec_state_h, dec_state_c = decoder(enc_outputs, decoder_inputs, dec_states_inputs)\n",
    "    # Dense Layer\n",
    "    dense_outputs = Dense(vocab_size, activation='softmax', name=\"dense\")(attention_output)\n",
    "    # seq2seq model\n",
    "    model = Model(inputs=[encoder_inputs, decoder_inputs], outputs=dense_outputs)\n",
    "\n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c390e4ff",
   "metadata": {},
   "source": [
    "训练的模型结构如下图。\n",
    "![trainingmodel](https://gitee.com/dotzhen/cloud-notes/raw/master/%E6%89%B9%E6%B3%A8%202021-11-20%20170856.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfa04e5c",
   "metadata": {},
   "source": [
    "2. decodeModel.py中预测部分模型代码如下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6499c6e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def encoder_infer(model):\n",
    "#     encoder_model = Model(inputs=model.get_layer('encoder').inputs,\n",
    "#                         outputs=model.get_layer('encoder').outputs)\n",
    "    encoder_model = Model(inputs=model.get_layer('encoder').get_input_at(0),\n",
    "                        outputs=model.get_layer('encoder').get_output_at(0))\n",
    "    return encoder_model\n",
    "\n",
    "\n",
    "def decoder_infer(model, encoder_model):\n",
    "    encoder_output = encoder_model.get_layer('encoder').output[0]\n",
    "    maxlen, hidden_units = encoder_output.shape[1:]\n",
    "\n",
    "    dec_input = model.get_layer('decode_input').input\n",
    "    enc_output = Input(shape=(maxlen, hidden_units), name='enc_output')\n",
    "    dec_input_state_h = Input(shape=(hidden_units,), name='input_state_h')\n",
    "    dec_input_state_c = Input(shape=(hidden_units,), name='input_state_c')\n",
    "    dec_input_states = [dec_input_state_h, dec_input_state_c]\n",
    "\n",
    "    decoder = model.get_layer('decoder')\n",
    "    dec_outputs, out_state_h, out_state_c = decoder(enc_output, dec_input, dec_input_states)\n",
    "    dec_output_states = [out_state_h, out_state_c]\n",
    "\n",
    "    decoder_dense = model.get_layer('dense')\n",
    "    dense_output = decoder_dense(dec_outputs)\n",
    "\n",
    "    decoder_model = Model(inputs=[enc_output, dec_input, dec_input_states],\n",
    "                          outputs=[dense_output] + dec_output_states)\n",
    "    return decoder_model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d51f2439",
   "metadata": {},
   "source": [
    "预测模型如下图。\n",
    "![predictmodel](https://gitee.com/dotzhen/cloud-notes/raw/master/%E6%89%B9%E6%B3%A8%202021-11-20%20170857.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f49f3c8c",
   "metadata": {},
   "source": [
    "### 3 调试系统"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b456604c",
   "metadata": {},
   "source": [
    "为了方便测试与调试系统写了一个简单的如下的网页："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58b55f23",
   "metadata": {},
   "outputs": [],
   "source": [
    "<html>\n",
    "<head>\n",
    "<title>我的第一个 HTML 页面</title>\n",
    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n",
    "</head>\n",
    "<body>\n",
    "<p>测试聊天机器。</p>\n",
    "<form action=\"http://127.0.0.1:8000/askinfo/\" method=\"get\">\n",
    "  <label for=\"lname\">问题：</label>\n",
    "  <input type=\"text\" id=\"query\" name=\"query\"><br><br>\n",
    "  <input type=\"submit\" value=\"使用 GET 提交\">\n",
    "  <input type=\"submit\" formmethod=\"post\" value=\"使用 POST 提交\">\n",
    "</form>\n",
    "</body>\n",
    "</html>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b095c939",
   "metadata": {},
   "source": [
    "截图如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6a043c8",
   "metadata": {},
   "source": [
    "![测试1](https://gitee.com/dotzhen/cloud-notes/raw/master/%E6%89%B9%E6%B3%A8%202021-11-20%20170852.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "922ed340",
   "metadata": {},
   "source": [
    "点击“使用POST提交”，显示如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2867dbcd",
   "metadata": {},
   "source": [
    "![测试2](https://gitee.com/dotzhen/cloud-notes/raw/master/%E6%89%B9%E6%B3%A8%202021-11-20%20170853.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da51b39f",
   "metadata": {},
   "source": [
    "系统运行情况如下："
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2c66c16",
   "metadata": {},
   "source": [
    "![result](https://gitee.com/dotzhen/cloud-notes/raw/master/%E6%89%B9%E6%B3%A8%202021-11-20%20170854.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4bc20f6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
